package cn.gaosiy.raf.core;

import cn.gaosiy.thread.RafThread;

import javax.swing.*;
import javax.swing.border.LineBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import java.net.HttpURLConnection;
import java.net.URL;

import java.util.UUID;

/**
 * 主启动类
 * @author gaosiy
 */
public class AppRunner {

    private static AppRunner appRunner = new AppRunner();

    /**
     * 开启多少线程
     */
    private int threadNum = 20;

    /**
     * 需要读取的文件，可以是网络文件
     */
    private File readFile;

    /**
     * 需要写入的地址，需要是实体地址
     */
    private File writeFile;

    /**
     * 缓存区大小
     */
    private int bufferSize = 1;

    /**
     * 下载的文件类型
     * 1 : 网络文件
     * 2 : 本地文件
     */
    private int fileType = 1;

    private boolean first = true;

    private void clear(){
        readFile = null;
        writeFile = null;
        bufferSize = 1;
    }

    private String downloadFile(String from, String to,int bufferSize,int fileType){
        long rLen = 0L;
        writeFile = new File(to);
        if(fileType == 2){
            readFile = new File(from);
            if(!readFile.exists()){
                return "下载文件不存在";
            }
            rLen = readFile.length();
        } else {
            URL url = null;
            try {
                url = new URL(from);
                //建立一个连接
                HttpURLConnection conn = (HttpURLConnection)url.openConnection();
                conn.setRequestMethod("GET");
                conn.setRequestProperty("Accept-Encoding", "identity");
                conn.connect();
                try {
                    rLen = Long.parseLong(conn.getHeaderField("Content-Length"));
                } catch (Exception e) {
                    rLen = -1;
                }
                conn.disconnect();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("下载文件总大小为: "+rLen+"，约为: "+(rLen / 1024 / 1024)+" MB");

        long tLT = rLen / threadNum;
        long tLT2 = rLen % threadNum;
        // 多读一次
        if(tLT2 > 0L){
            threadNum++;
        }
        long blockSize = 0L;
        long startPos = 0L;
        long endPos = 0L;
        long lastEndPos = 0L;
        for (int i = 0; i < threadNum; i++){
            if((i+1) == threadNum){
                blockSize = tLT2 == 0L ? tLT : tLT2;
                startPos = lastEndPos + 1;
                endPos = rLen;
            } else {
                blockSize = tLT2 == 0L ? tLT : tLT;
                startPos = i * blockSize;
                endPos = (i + 1) * blockSize -1;
                lastEndPos = endPos;
            }
            RafThread rafThread = new RafThread(from, writeFile, startPos, endPos, blockSize, bufferSize, fileType);
            if(i < 10){
                rafThread.setName("Thread"+i+" ");
            } else {
                rafThread.setName("Thread"+i);
            }
            rafThread.start();
        }
        return "下载成功";
    }

    /**
     * 使用hutool 下载文件
     * @param url
     * @param dest
     */
    /*private void hutoolDown(String url,File dest){
        HttpUtil.downloadFile(url,dest);
    }*/

    /**
     * swing 画板添加组件
     * @param jPanel
     */
    private void placeComponents(JPanel jPanel) {
        // 不设置布局
        jPanel.setLayout(null);

        // 要下载文件网络地址
        JLabel fileNetLabel = new JLabel("文件地址:");
        fileNetLabel.setBounds(17,20,150,20);
        jPanel.add(fileNetLabel);

        // 要下载到本机哪里去
        JLabel fileLocationLabel = new JLabel("本地地址:");
        fileLocationLabel.setBounds(17,50,150,20);
        jPanel.add(fileLocationLabel);

        // 路径
        JTextField fileLocationText = new JTextField(20);
        fileLocationText.setBounds(177,50,280,20);
        fileLocationText.setEditable(false);
        jPanel.add(fileLocationText);

        JTextField fileLocationNameText = new JTextField(20);
        fileLocationNameText.setBounds(460,50,50,20);
        fileLocationNameText.setEditable(false);
        jPanel.add(fileLocationNameText);

        JTextField fileNetText = new JTextField(20);
        fileNetText.setBounds(177,20,447,20);
        fileNetText.setToolTipText("目前只能使用 本地与 http形式下载，其他协议暂不支持");
        fileNetText.getDocument().addDocumentListener(new DocumentListener() {
            @Override
            public void insertUpdate(DocumentEvent e) {
                updateFileName(e);
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                updateFileName(e);
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                updateFileName(e);
            }

            private void updateFileName(DocumentEvent e){
                if(e.getDocument().getLength() == 0){
                    return;
                }
                // 网络/本地 文件地址
                String netText = fileNetText.getText();
                if(!netText.isEmpty()){
                    fileType = netText.indexOf("http://") == -1 ? netText.indexOf("https://") == -1 ? 2 : 1 : 1;
                    netText = netText.replaceAll("\\\\","/");
                    int lastIndexOf = netText.lastIndexOf("/");
                    if(lastIndexOf != -1){
                        String fileSuffix = netText.substring(netText.lastIndexOf("."));
                        String fileName = fileType == 1 ? netText.substring(lastIndexOf) : "/"+UUID.randomUUID() +fileSuffix;
                        fileLocationNameText.setText(fileName);
                        fileLocationNameText.setToolTipText(fileName);
                    }
                }
            }

        });
        jPanel.add(fileNetText);

        JCheckBox fileCanOverride = new JCheckBox();
        fileCanOverride.setBounds(510,50,20,20);
        fileCanOverride.setToolTipText("选中可自定义文件名");
        fileCanOverride.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                if(e.getStateChange() == ItemEvent.SELECTED){
                    fileLocationNameText.setEditable(true);
                } else {
                    fileLocationNameText.setEditable(false);
                }
            }
        });
        jPanel.add(fileCanOverride);

        // 预览button 与 file choose
        JFileChooser file = new JFileChooser();
        JButton preview = new JButton("保存到");
        preview.setBounds(532,50,90,20);
        preview.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                file.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES );
                int fileSelect = file.showDialog(new JLabel(), "选择");
                if(fileSelect == JFileChooser.APPROVE_OPTION){
                    File selectedFile = file.getSelectedFile();
                    // 选择的本地文件夹地址
                    String filePath = selectedFile.getAbsolutePath();
                    if(!filePath.isEmpty()){
                        filePath = filePath.replaceAll("\\\\","/");
                    }
                    fileLocationText.setText(filePath);
                }
            }
        });
        jPanel.add(preview);

        // 缓存区
        JLabel bufferLabel = new JLabel("循环缓存区大小:");
        bufferLabel.setBounds(17,80,150,20);
        jPanel.add(bufferLabel);

        JComboBox bufferCmd = new JComboBox();
        bufferCmd.setToolTipText("缓存区与线程数有一定关系");
        bufferCmd.addItem("1MB");
        bufferCmd.addItem("2MB");
        bufferCmd.addItem("3MB");
        bufferCmd.addItem("4MB");
        bufferCmd.addItem("5MB");
        bufferCmd.setBounds(177,80,447,20);
        bufferCmd.setSelectedIndex(0);
        bufferCmd.addItemListener(new ItemListener(){
            @Override
            public void itemStateChanged(ItemEvent e) {
                if(e.getStateChange() == ItemEvent.SELECTED){
                    String item = String.valueOf(e.getItem());
                    String[] mbs = item.split("MB");
                    bufferSize = Integer.parseInt(mbs[0]);
                }
            }
        });
        jPanel.add(bufferCmd);

        // 线程数
        JLabel threadNumLabel = new JLabel("线程数:");
        threadNumLabel.setBounds(17,110,150,20);
        jPanel.add(threadNumLabel);

        JTextField threadNumText = new JTextField(20);
        threadNumText.setBounds(177,110,447,20);
        threadNumText.setText(String.valueOf(threadNum));
        threadNumText.setToolTipText("并非越大越好，机器无法承受就没办法了，尽量小一点");
        jPanel.add(threadNumText);

        JTextField resultText = new JTextField(20);
        resultText.setBounds(17,140,607,20);
        jPanel.add(resultText);

        JButton downButton = new JButton("下载");
        downButton.setBounds(17,170,607,20);
        downButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if(!first){
                    appRunner.clear();
                    bufferCmd.setSelectedIndex(0);
                }
                // 下载地址与下载到地址不能为空
                String netText = fileNetText.getText();
                if(netText.isEmpty()){
                    fileNetText.setBorder(new LineBorder(Color.RED,1,true));
                    resultText.setText("下载地址不能为空");
                    return;
                } else {
                    fileNetText.setBorder(null);
                }
                String locationText = fileLocationText.getText();
                if(locationText.isEmpty()){
                    fileLocationText.setBorder(new LineBorder(Color.RED,1,true));
                    resultText.setText("下载到本地的地址不能为空");
                    return;
                } else {
                    fileLocationText.setBorder(null);
                }
                String threadNum = threadNumText.getText();
                if(!threadNum.isEmpty()){
                    try {
                        appRunner.threadNum  = Integer.parseInt(threadNum);
                    } catch (Exception e2) {
                        threadNumText.setText("");
                        threadNumText.setBorder(new LineBorder(Color.RED,1,true));
                        resultText.setText("输入线程数不是数字");
                        return;
                    }
                }
                String fileLocationName = fileLocationNameText.getText();
                if(fileLocationName.isEmpty()){
                    fileLocationNameText.setBorder(new LineBorder(Color.RED,1,true));
                    resultText.setText("需要保存的文件名不存在");
                    return;
                }
                long start = System.currentTimeMillis();
                String result = appRunner.downloadFile(netText, fileLocationText.getText() + fileLocationName, bufferSize, fileType);
                long end = System.currentTimeMillis();
                resultText.setText("结果: "+result+"，耗时: "+(end - start) + " ms");
                first = false;
            }
        });
        jPanel.add(downButton);

    }

    public static void main(String[] args) {
        JFrame jFrame = new JFrame("RAF多线程下载文件");
        // 宽高
        jFrame.setSize(655,250);
        // 居中
        jFrame.setLocationRelativeTo(null);

        // 关闭样式
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // 画板
        JPanel jPanel = new JPanel();
        jFrame.add(jPanel);

        // 画板添加组件
        appRunner.placeComponents(jPanel);

        // 可见
        jFrame.setVisible(true);
    }

}
